/*****************************************************************************
 *
 * @file:	D:\prj\git-version\BB_sensor_IMU\IMU\drivers\QMC5883L\QMC5883.c
 * @author: Xiaodong Zhang
 * @Email:	sacntumz@foxmail.com;zhangxiaodong@beyondbit.com
 * @date:	2020/06
 * @brief:  QMC5883 driver file

******************************************************************************
*
* Revision History:
*
* Rev.  Date        Who       		Changes
* 1     2020/06   Xiaodong Zhang  		New Created.
******************************************************************************

******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2020 BeyondBit</center></h2>
  *
  *
******************************************************************************/

 
/* Includes ------------------------------------------------------------------*/
#include "QMC5883.h"
#include "i2c.h"
/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
static struct geomag_qmc5883 qmc_driver; 

/* Private function prototypes -----------------------------------------------*/
static BB_StatusTypeDef QMC5883_ReadData(uint8_t reg_addr,uint8_t *rdata_ptr,uint8_t rlen);
static BB_StatusTypeDef QMC5883_WriteReg(uint8_t reg_addr,uint8_t *wdata_ptr,uint8_t wlen);  
static float qmc_get_real_fsr_factor(void);
/* Private functions ---------------------------------------------------------*/
 

/** @addtogroup 
  * @{
  */

/** @addtogroup 
  * @{
  */

/**
  * @brief  QMC5883 initial
  * @param	void
  * @retval status
 */ 
BB_StatusTypeDef bb_qmc5883_init(void)
{
	uint8_t regval = 0;

	BB_StatusTypeDef status = BB_NO_ERROR;

	// mag_x_buff[MAFWINDOWSIZE] = {0};
	// mag_y_buff[MAFWINDOWSIZE] = {0};
	// mag_z_buff[MAFWINDOWSIZE] = {0};

	qmc_driver.context = NULL;
	qmc_driver.read_reg = QMC5883_ReadData;
	qmc_driver.write_reg = QMC5883_WriteReg;

	//inital data status according to datasheet
	qmc_driver.curr_setting.curr_workmode = QMC5883_STANDBY;
	qmc_driver.curr_setting.curr_fsr = QMC5883_FSR_2GAUSS;
	qmc_driver.curr_setting.curr_odr = QMC5883_ODR_10HZ;
	qmc_driver.curr_setting.curr_osr = QMC5883_OSR_512;
	qmc_driver.curr_setting.curr_int = QMC5883_INT_DIS;
	qmc_driver.curr_setting.curr_rol = QMC5883_ROLPNT_DIS;

	//get chip id
	status = QMC5883ReadID(&regval) ;
	if(status != BB_NO_ERROR){
		BB_MSG(BB_MSG_LEVEL_ERROR,"Hal ERROR When Read QMC5883 ID:0x%2x",regval);
		return status;
	}

	if(regval != 0xFF){
		BB_MSG(BB_MSG_LEVEL_ERROR,"Read QMC5883 ID ERROR, SHOULD BE 0XFF, But 0x%2x",regval);
		return BB_MAGNET_ERROR;
	}
	else if(regval == 0xFF)
		BB_MSG(BB_MSG_LEVEL_INFO,"Read QMC5883 ID OK");
	
	//reset chip
	status = QMC5883ChipReset();
	if(status!= BB_NO_ERROR){
		BB_MSG(BB_MSG_LEVEL_ERROR,"Hal ERROR When Reset QMC5883");
		return status;
	}

	status = QMC5883_ctrl1_config(QMC5883_CONTINUE,
																QMC5883_ODR_10HZ,
																QMC5883_FSR_2GAUSS,
                								QMC5883_OSR_64);
	if(status != BB_NO_ERROR){
		BB_MSG(BB_MSG_LEVEL_ERROR,"QMC5883 Ctrl1 config fail");		
		return status;
	}

	status = QMC5883_int_rol_config(QMC5883_INT_EN,QMC5883_ROLPNT_DIS);

	if(status != BB_NO_ERROR){
		BB_MSG(BB_MSG_LEVEL_ERROR,"QMC5883 Ctrl2 config fail");		
		return status;
	}

	HAL_Delay(10);
	return status;
}

/**
  * @brief  5883 interrupt and rol configuration
  * @param  
	* 			- QMC5883_INT_STATUS_t int_status,
	*				- QMC5883_ROLPNT_STATUS_t rol_status           
  * @retval status
 */ 
BB_StatusTypeDef QMC5883_ctrl1_config(QMC5883_WORKMODE_t workmode,
																QMC5883_ODR_t odr,
																QMC5883_FSR_t fsr,
																QMC5883_OSR_t osr)
{
	BB_StatusTypeDef status = BB_NO_ERROR;
	uint8_t regval = 0x00;

	regval = workmode & odr & fsr & osr;
	if(regval == 0xFF)
		return BB_ERROR_BAD_ARG;

	status = qmc_driver.read_reg(QMC5883_CTRL_1,&regval,1);
	if(status != BB_NO_ERROR)
		{		
			BB_MSG(BB_MSG_LEVEL_ERROR,"QMC5883 Ctrl1 Read Fail: %d",status);
			return status;
		}	
	
	if(workmode != QMC5883_WM_IGNOR)
		{
			regval =	(regval & QMC5883_WM_CLR) | workmode;
			qmc_driver.curr_setting.curr_workmode = workmode;
		}

	if(odr != QMC5883_ODR_IGNOR)
		{
			regval =	(regval & QMC5883_ODR_CLR) | odr;
			qmc_driver.curr_setting.curr_odr = odr;
		}
	
	if(fsr != QMC5883_FSR_IGNOR)
		{
			regval =	(regval & QMC5883_FSR_CLR) | fsr;
			qmc_driver.curr_setting.curr_fsr = fsr;
		}
	
	if(osr != QMC5883_OSR_IGNOR)
		{
			regval =	(regval & QMC5883_OSR_CLR) | osr;
			qmc_driver.curr_setting.curr_osr = osr;
		}
	

	status = qmc_driver.write_reg(QMC5883_CTRL_1,&regval,1);

	if(status != BB_NO_ERROR)
		{		
			BB_MSG(BB_MSG_LEVEL_ERROR,"QMC5883 Ctrl1 Write Fail: %d",status);
			return status;
		}
	return status;
}																

/**
  * @brief  5883 interrupt and rol configuration
  * @param  
	* 			- QMC5883_INT_STATUS_t int_status,
	*				- QMC5883_ROLPNT_STATUS_t rol_status           
  * @retval status
 */ 
BB_StatusTypeDef QMC5883_int_rol_config(QMC5883_INT_STATUS_t int_status,
																				QMC5883_ROLPNT_STATUS_t rol_status)
{
	BB_StatusTypeDef status = BB_NO_ERROR;
	uint8_t regval = 0;

	if((int_status == QMC5883_INT_IGNOR)&&(rol_status == QMC5883_ROLPNT_IGNOR))
		return status;

	status = qmc_driver.read_reg(QMC5883_CTRL_2,&regval,1);
	if(status != BB_NO_ERROR)
		return status;

	if(int_status == QMC5883_INT_IGNOR)//donnt touch while ignore
	{
		if(int_status == QMC5883_INT_EN)
			{
				regval |= int_status;
				qmc_driver.curr_setting.curr_int = QMC5883_INT_EN;
			}
		else if(int_status == QMC5883_INT_DIS)
			{
				regval &= int_status;
				qmc_driver.curr_setting.curr_int = QMC5883_INT_DIS;
			}
		else
		return BB_ERROR_BAD_ARG;
	}
	else if(rol_status == QMC5883_ROLPNT_IGNOR)//donnt touch while ignore
	{
		if(rol_status == QMC5883_ROLPNT_EN)
			{
				regval |= rol_status;
				qmc_driver.curr_setting.curr_rol = QMC5883_ROLPNT_EN;
			}
		else if(rol_status == QMC5883_ROLPNT_DIS)
			regval &= rol_status;
		else
			return BB_ERROR_BAD_ARG;
	}
	
	return qmc_driver.write_reg(QMC5883_CTRL_2,&regval,1);
}


/**
  * @brief  5883 chipreset
  * @param              
  * @retval 
 */ 
BB_StatusTypeDef QMC5883ChipReset(void)
{
  uint8_t regval = QMC5883ResetByte;
	return qmc_driver.write_reg(QMC5883_CTRL_2,&regval,1);
}

/**
  * @brief  5883read id
  * @param  point to chip id
  * @retval chipid point
 */
BB_StatusTypeDef QMC5883ReadID(uint8_t *chipid)
{
	return qmc_driver.read_reg(QMC5883_CHIP_ID, chipid,1);
}

/**
  * @brief  check and return data status
  * @param  void
  * @retval 5883 status register 1 content, 0XFF error
 */ 
uint8_t QMC5883_check_datastatus(void)
{
	uint8_t rc = 0xFF;
	BB_StatusTypeDef status = BB_NO_ERROR;

	status = qmc_driver.read_reg(QMC5883_STATUS,&rc,1);
	if(status != BB_NO_ERROR)
		{
			BB_MSG(BB_MSG_LEVEL_ERROR,"Check datastatus error:%d",status);
			return 0xFF;
		}

	return rc;
}

/**
  * @brief  QMC5883 get magnet data
	* 				- check status register and get data rdy ,overflow and data skip
	*					- read data out and formate into int16_t
  * @param:	int16_t* MagnetRawAd
  * @retval BB status
*/ 
BB_StatusTypeDef QMC5883_GetData(int16_t* MagnetRawAd)
{
	uint8_t data_status = 0x00;
	uint8_t Buff[6];

	data_status = QMC5883_check_datastatus();

	while((data_status & QMC5883_DRDY_MASK) == 0x00)//no new data in register
	{
		HAL_Delay(2);
		data_status = QMC5883_check_datastatus();
		//delay 2ms
		//TODO the best way is to auto change this time interval accordinig to odr
		
	}
	
	if(data_status & QMC5883_OVL_MASK){
		BB_MSG(BB_MSG_LEVEL_WARNING,"QMC Data Overflow.");
		//TODO should add sth to handle this situation
	}

	if(data_status & QMC5883_OVL_MASK){
		BB_MSG(BB_MSG_LEVEL_WARNING,"QMC Data Skiped.");
		//TODO should add sth to handle this situation
	}

	// read data out
	QMC5883_ReadData(QMC5883_XL_OUT,Buff,6);

  MagnetRawAd[0] = ((int16_t)Buff[1] << 8) | Buff[0];
  MagnetRawAd[1] = ((int16_t)Buff[3] << 8) | Buff[2];
  MagnetRawAd[2] = ((int16_t)Buff[5] << 8) | Buff[4];


	return BB_NO_ERROR;
}

/**
  * @brief  Get transform factor for Guass to uT 
  * @param:	void
	* @retval float factor
*/ 
float	get_mag_trans_fac(void)
{
	return qmc_get_real_fsr_factor();
}

/**
  * @brief  QMC5883 read data throught i2c
  * @param:	uint8_t reg_addr: register address
	* 				uint8_t *rdata_ptr: read buffer pointer
	*					uint8_t rlen: read length
  * @retval BB status
*/ 
static BB_StatusTypeDef QMC5883_ReadData(uint8_t reg_addr,uint8_t *rdata_ptr,uint8_t rlen)
{
	HAL_StatusTypeDef hal_status = HAL_OK;

  hal_status = sensor_i2c_readData((uint16_t)QMC5883_SLAVE_ADDRESS,
  										(uint16_t)reg_addr,
  										rdata_ptr,
  										(uint16_t)rlen);

	if (hal_status == HAL_ERROR)
	  {
	    BB_MSG(BB_MSG_LEVEL_ERROR,"I2C HAL ERROR. %s Line:%d",__FILE__,__LINE__);
	    return BB_ERROR_HW;
	  }
	else if (hal_status == HAL_BUSY)
	  {
	    BB_MSG(BB_MSG_LEVEL_ERROR,"I2C HAL BUSY.  %s Line:%d",__FILE__,__LINE__);      
	    return BB_ERROR_HW;
	  }
	else if (hal_status == HAL_TIMEOUT)
	  {
	    BB_MSG(BB_MSG_LEVEL_ERROR,"I2C HAL TIMEOUT. %s Line:%d",__FILE__,__LINE__);
	    return BB_ERROR_TIMEOUT;
	  }

	return BB_NO_ERROR;
}


/**
  * @brief  QMC5883 write data throught i2c
  * @param	uint8_t reg_addr: register address
	* 				uint8_t *wdata_ptr: read buffer pointer
	*					uint8_t wlen: read length
  * @retval BB status
 */ 
static BB_StatusTypeDef QMC5883_WriteReg(uint8_t reg_addr,uint8_t *wdata_ptr,uint8_t wlen)
{
	HAL_StatusTypeDef hal_status = HAL_OK;

	hal_status = sensor_i2c_writeData(QMC5883_SLAVE_ADDRESS,
																		(uint16_t)reg_addr,
																		wdata_ptr,
																		(uint16_t)wlen);

	if (hal_status == HAL_ERROR)
	  {
	    BB_MSG(BB_MSG_LEVEL_ERROR,"I2C HAL ERROR in Write. %s Line:%d",__FILE__,__LINE__);
	    return BB_ERROR_HW;
	  }
	else if (hal_status == HAL_BUSY)
	  {
	    BB_MSG(BB_MSG_LEVEL_ERROR,"I2C HAL BUSY in Write.  %s Line:%d",__FILE__,__LINE__);      
	    return BB_ERROR_HW;
	  }
	else if (hal_status == HAL_TIMEOUT)
	  {
	    BB_MSG(BB_MSG_LEVEL_ERROR,"I2C HAL TIMEOUT in Write. %s Line:%d",__FILE__,__LINE__);
	    return BB_ERROR_TIMEOUT;
	  }
	else
		return BB_NO_ERROR;
} 

/**
  * @brief  get fsr factor which will be used to generate uT vaule
  * @param	void
  * @retval 120,30 when error return -1
 */ 
static float qmc_get_real_fsr_factor(void)
{
	switch(qmc_driver.curr_setting.curr_fsr) {
	case QMC5883_FSR_2GAUSS:	return 0.0083f;// 1/120
	case QMC5883_FSR_8GAUSS:  return 0.0333f;// 1/80
	default:return -1;
	}

}

/**
  * @}
  */

/**
  * @}
  */



/*****END OF FILE****/


